﻿using System;
using System.Data;
using System.Text;
using System.Collections.Generic;


namespace WinFormClient
{
    public class MapXml
    {
        #region namespace
        static string _namespace;
        public static string NameSpace
        {
            get { return _namespace; }
            set { _namespace = value; }
        }
        #endregion
        #region sequence
        static string _sequence;
        public static string Sequence
        {
            get { return _sequence; }
            set { _sequence = value; }
        }
        #endregion

        #region database
        static IDataBase _dataBase;

        public static IDataBase DataBase
        {
            get { return _dataBase; }
            set { _dataBase = value; }
        }
        #endregion

        #region daofactory,sqlmaps
        static string _daoFactory = "";
        static string _sqlMaps = "";
        public static string DaoFactory
        {
            get { return _daoFactory; }
        }

        public static string SqlMaps
        {
            get { return _sqlMaps; }
        }

        /// <summary>
        /// clear the existed daoFactory/sqlMaps value;
        /// </summary>
        public static void Reset()
        {
            _daoFactory = "";
            _sqlMaps = "";
        }
        #endregion

        private string _tableName;
        private string _className;
        private ColumnMapCollection _col;
        private int _lenghtForIgnoreParameter = 500;
        private MethodCollection _methods = new MethodCollection();

        public MapXml(string tableName,string className, int lengthForIgnoreParameter, ColumnMapCollection col)
        {
            this._tableName = tableName;
            this._className = className;
            this._col = col;
            this._lenghtForIgnoreParameter = lengthForIgnoreParameter;
            if (this._col.Count == 0) new Exception("dataObject has not columns.");
        }

        public MapXml(string tableName, string className, ColumnMapCollection col)
        {
            this._tableName = tableName; //表名
            this._className = className; //类名
            this._col = col; //字段集合
            if (this._col.Count == 0) new Exception("dataObject has not columns.");
        }

        public void Builder(bool getAll, bool getByPrimaryKey, bool getWithColumns, bool getWithUnlimited, bool create, bool delete, bool deleteByPrimaryKey, bool deleteWithColumns, bool deleteWithUnlimited, bool update, bool updateWithUnlimited)
        {
            string content
            = this.header() //头部
            + this.Alias()  //别名 
            + this.ResultMaps() //字段映射
            + this.ParameterMap() //存储过程的参数
            + Environment.NewLine
            + "  <statements>" + Environment.NewLine //操作语句
            + ((create)?this.Create():"") //创建
            + ((getAll)?this.SelectAll():"") //全选
            + ((getByPrimaryKey)?this.SelectByPrimaryKey():"") //根据ID返回一个值
            + ((getWithColumns)?this.SelectByParameters():"") //根据几个字段返回若干个值
            + ((getWithUnlimited)?this.SelectByUnlimitedParameters():"")  //??
            + ((update)?this.UpdateByEntry():"") //更新？？
            + ((updateWithUnlimited)?this.UpdateByUnlimitedParameters():"")//??
            + ((delete)?this.DeleteByEntry():"") //删除 ？？
            + ((deleteByPrimaryKey)?this.DeleteByPrimaryKey():"") //根据ID删除
            + ((deleteWithColumns)?this.DeleteByParameters():"") //根据若干字段删除若干值
            + ((deleteWithUnlimited)?this.DeleteByUnlimitedParameters():"") //??
            + "  </statements>" + Environment.NewLine
            + "</sqlMap>"
            ;

            Tools.TextSaveToFile(content, "Xml\\" + MapXml.DataBase.NAME, this._className + ".xml"); //保存XML文件
            MapXml._daoFactory += "      <dao interface=\"" + MapXml._namespace + ".Interface.I" + this._className + "," + MapXml._namespace + "\" implementation=\"" + MapXml._namespace + ".Dao." + this._className + "," + MapXml._namespace + "\" />" + Environment.NewLine;
            MapXml._sqlMaps += "    <sqlMap resource=\"Xml\\" + MapXml._dataBase.NAME + "\\" + this._className + ".xml\" />" + Environment.NewLine;
        }

        public MethodCollection Methods
        {
            get { return this._methods; }
        }

        private string header()
        {
            return
                  "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" + System.Environment.NewLine
                + "<sqlMap namespace=\"" + this._className + "\" xmlns=\"http://ibatis.apache.org/mapping\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" >" + System.Environment.NewLine
            ;
        }

        private string Alias()
        {
            return
                   "  <alias>" + System.Environment.NewLine
                + "    <typeAlias alias=\"" + this._className + "\" type=\"" + _namespace + ".Model." + this._className + "," + _namespace + "\" />" + System.Environment.NewLine
                + "  </alias>" + System.Environment.NewLine
            ;
        }

        private string ResultMaps()
        {
            StringBuilder sb = new StringBuilder();
            sb.Append(
                "  <resultMaps>" + Environment.NewLine
                + "    <resultMap id=\"" + this._tableName + "Result\" class=\"" + this._className + "\">" + Environment.NewLine
                );
            ColumnMap map = null;
            for (int i = 0; i < this._col.Count; i++)
            {
                map = this._col[i];
                sb.Append("      <result property=\"" + map.PropertyName + "\" column=\"" + map.ColumnName + "\" />" + Environment.NewLine);
            }
            sb.Append("    </resultMap>" + Environment.NewLine);
            sb.Append("  </resultMaps>" + Environment.NewLine);
            return sb.ToString();
        }

        private string ParameterMap()
        {
            StringBuilder sb = new StringBuilder();
            sb.Append(
                "  <parameterMaps>" + Environment.NewLine
                + "    <parameterMap id=\"" + this._tableName + "Parameter\" class=\"" + this._className + "\">" + Environment.NewLine
                );
            ColumnMap map = null;

            for (int i = 0; i < this._col.Count; i++)
            {
                map = this._col[i];
                sb.Append("      <parameter property=\"" + map.PropertyName + "\" column=\"" + map.ColumnName + "\" />" + Environment.NewLine);
            }
            sb.Append("    </parameterMap>" + Environment.NewLine);
            sb.Append("  </parameterMaps>" + Environment.NewLine);
            return sb.ToString();
        }

        private string Create()
        {
            this._methods.Add(new Method("object", "Create", this._tableName + "_Create", "Model." + this._className, Tools.ParameterConvert(this._className), DataAccessMethod.ExecuteInsert));

            StringBuilder sb = new StringBuilder();
            sb.Append("    <insert id=\"" + this._tableName + "_Create\"  parameterClass=\"" + this._className + "\" >" + Environment.NewLine);
            if (DataBase.IdentityPosition == Position.createStatementBefore)
            {
                if (this._col.PrimaryKeyColumn != null)
                {
                    sb.Append("    <selectKey resultClass=\"" + this._col.PrimaryKeyColumn.CsDataType + "\" type=\"pre\" property=\"" + this._col.PrimaryKeyColumn.PropertyName + "\" >" + Environment.NewLine);
                    sb.Append("      " + DataBase.Identity(Sequence) + Environment.NewLine);
                    sb.Append("    </selectKey>" + Environment.NewLine);
                }
            }
            sb.Append("      INSERT INTO " + this._tableName + " (");
            int count = this._col.Count;
            for (int i = 0; i < count; i++) sb.Append(this._col[i].ColumnName + ((i == (count - 1)) ? "" : ","));
            sb.Append(")");
            sb.Append(Environment.NewLine);
            sb.Append("      VALUES (");
            for (int i = 0; i < count; i++) sb.Append("#" + this._col[i].PropertyName + "#" + ((i == (count - 1)) ? "" : ","));
            sb.Append(")");
            sb.Append("    </insert>" + Environment.NewLine);
            if (DataBase.IdentityPosition == Position.createStatementAfter)
            {
                if (this._col.PrimaryKeyColumn != null)
                {
                    sb.Append("    <selectKey resultClass=\"" + this._col.PrimaryKeyColumn.CsDataType + "\" type=\"post\" property=\"" + this._col.PrimaryKeyColumn.PropertyName + "\" >" + Environment.NewLine);
                    sb.Append("      " + DataBase.Identity(Sequence) + Environment.NewLine);
                    sb.Append("    </selectKey>" + Environment.NewLine);
                }
            }
            return sb.ToString();
        }

        #region select
        private string SelectAll()
        {
            this._methods.Add(new Method("Model." + this._className, "Get", this._tableName + "_SelectAll", null, null, DataAccessMethod.ExecuteQueryForList));
            return this.Select("", this._tableName + "_SelectAll", null);
        }

        private string SelectByPrimaryKey()
        {
            StringBuilder sb = new StringBuilder();

            //the first only one column's primaryKey
            if (this._col.PrimaryKeyColumn != null)
            {
                this._methods.Add(new Method("Model." + this._className, "Get", this._tableName + "_SelectByPrimaryKey", this._col.PrimaryKeyColumn.CsDataType, this._col.PrimaryKeyColumn.FieldName.Replace("_",""), DataAccessMethod.ExecuteQueryForObject));
                sb.Append(this.Select(this.PrimaryKeyWhere(), this._tableName + "_SelectByPrimaryKey", this._col.PrimaryKeyColumn.CsDataType));
            }

            //the second primaryKey
            SortedList<string, string> pklist = new SortedList<string, string>();
            for (int i = 0; i < this._col.Count;i++ )
            {
                ColumnMap map = this._col[i];
                if (map != this._col.PrimaryKeyColumn)
                {
                    if (!String.IsNullOrEmpty(map.IndexName))
                    {
                        if (!pklist.ContainsKey(map.IndexName) )
                        {
                            pklist.Add(map.IndexName, i.ToString());
                        }
                        else
                        {
                            pklist[map.IndexName] +=  "," + i.ToString();
                        }
                    }
                }
            }

            if (pklist.Count > 0)
            {
                for (int j = 0; j < pklist.Count; j++)
                {
                    ColumnMapCollection col = new ColumnMapCollection();
                    StringBuilder pk = new StringBuilder();
                    pk.Append("      <dynamic prepend=\"WHERE\">" + Environment.NewLine);      
                    int x = 0;
                    foreach (string v in pklist.Values[j].Split(','))
                    {
                        if(x == 0) pk.Append(" AND ");
                        x ++;
                        pk.Append("          " + this._col[Int32.Parse(v)].ColumnName + " = #" + this._col[Int32.Parse(v)].PropertyName + "#" );          
                        col.Add(this._col[Int32.Parse(v)]);
                    }
                    pk.Append(Environment.NewLine);
                    pk.Append("      </dynamic>" + Environment.NewLine);

                    this._methods.Add(new Method("Model." + this._className, "Get", this._tableName + "_SelectBy" + pklist.Keys[j], "Parameters." + this._className + "By" + pklist.Keys[j], Tools.FieldConvert(pklist.Keys[j]).Replace("_", ""), DataAccessMethod.ExecuteQueryForObject));
                    sb.Append(this.Select(pk.ToString(),this._tableName+"_SelectBy" + pklist.Keys[j], _namespace + ".Parameters." + this._className + "By" + pklist.Keys[j] + "," + _namespace));
                    new MultiColumnsPKParameters(this._tableName, pklist.Keys[j], col).Builder();
                }
            }

            return sb.ToString();
        }

        private string SelectByParameters()
        {
            this._methods.Add(new Method("Model." + this._className, "Get", this._tableName + "_SelectByParameters", "Parameters." + this._className + "Parameters", Tools.ParameterConvert(this._className) + "Parameters", DataAccessMethod.ExecuteQueryForList));
            return this.Select(this.ParameterWhere(), this._tableName + "_SelectByParameters", _namespace + ".Parameters." + this._className + "Parameters," + _namespace);
        }

        private string SelectByUnlimitedParameters()
        {
            this._methods.Add(new Method("Model." + this._className, "GetAnyWhere", this._tableName + "_SelectByUnlimitedParameters", "Parameters." + this._className + "Parameters", Tools.ParameterConvert(this._className) + "Parameters", DataAccessMethod.ExecuteQueryForList));
            return this.Select(this.UnlimitedWhere(), this._tableName + "_SelectByUnlimitedParameters", _namespace + ".Parameters." + this._className + "Parameters," + _namespace);
        }

        private string Select(string where, string id, string paramClass)
        {
            if (where == null) return "";

            StringBuilder sb = new StringBuilder();
            sb.Append(
                "    <select id=\"" + id + "\" resultMap=\"" + this._tableName + "Result\"" + ((String.IsNullOrEmpty(paramClass)) ? "" : (" parameterClass=\"" + paramClass + "\"")) + ">" + Environment.NewLine
               + "      SELECT * FROM " + this._tableName + Environment.NewLine
               );
            sb.Append(where);
            sb.Append("    </select>" + Environment.NewLine);

            return sb.ToString();
        }
        #endregion

        #region delete
        private string DeleteByEntry()
        {
            if (this._col.PrimaryKeyColumn == null) return "";
            this._methods.Add(new Method("object", "Delete", this._tableName + "_DeleteByEntry", "Model." + this._className, Tools.ParameterConvert(this._className), DataAccessMethod.ExecuteDelete));
            return this.Delete(this.EntryWhere(), this._tableName + "_DeleteByEntry", this._className);
        }

        private string DeleteByPrimaryKey()
        {
            if (this._col.PrimaryKeyColumn == null) return "";
            this._methods.Add(new Method("object", "Delete", this._tableName + "_DeleteByPrimaryKey", this._col.PrimaryKeyColumn.CsDataType, Tools.FieldConvert(this._col.PrimaryKeyColumn.PropertyName).Replace("_",""), DataAccessMethod.ExecuteDelete));
            return this.Delete(this.PrimaryKeyWhere(), this._tableName + "_DeleteByPrimaryKey", this._col.PrimaryKeyColumn.CsDataType);
        }

        private string DeleteByParameters()
        {
            this._methods.Add(new Method("object", "Delete", this._tableName + "_DeleteByParameters", "Parameters." + this._className + "Parameters", Tools.ParameterConvert(this._className) + "Parameters", DataAccessMethod.ExecuteDelete));
            return this.Delete(this.ParameterWhere(), this._tableName + "_DeleteByParameters", _namespace + ".Parameters." + this._className + "Parameters," + _namespace);
        }

        private string DeleteByUnlimitedParameters()
        {
            this._methods.Add(new Method("object", "DeleteAnyWhere", this._tableName + "_DeleteByUnlimitedParameters", "Parameters." + this._className + "Parameters", Tools.ParameterConvert(this._className) + "Parameters", DataAccessMethod.ExecuteDelete));
            return this.Delete(this.UnlimitedWhere(), this._tableName + "_DeleteByUnlimitedParameters", _namespace + ".Parameters." + this._className + "Parameters," + _namespace);
        }

        private string Delete(string where, string id, string paramClass)
        {
            if (String.IsNullOrEmpty(where)) return "";

            return
                "    <delete id=\"" + id + "\" parameterClass=\"" + paramClass + "\">" + Environment.NewLine
                + "      <![CDATA[ " + Environment.NewLine
                + "        DELETE " + this._tableName + Environment.NewLine
                + "      ]]>" + Environment.NewLine
                + where
                + "    </delete>" + Environment.NewLine
                ;
        }
        #endregion

        #region Update
        private string UpdateByEntry()
        {
            if(this._col.PrimaryKeyColumn == null) return "";
            this._methods.Add(new Method("int", "Update", this._tableName + "_UpdateByEntry", "Model." + this._className, Tools.ParameterConvert(this._className), DataAccessMethod.ExecuteUpdate));
            return this.Update(this.EntryWhere(), this._tableName + "_UpdateByEntry", this._className);
        }

        private string UpdateByUnlimitedParameters()
        {
            if (this._col.PrimaryKeyColumn == null) return "";

            this._methods.Add(new Method("int", "Update", this._tableName + "_UpdateByUnlimitedParameters", "Parameters." + this._className + "Parameters", Tools.ParameterConvert(this._className) + "Parameters", DataAccessMethod.ExecuteUpdate));

            StringBuilder sb = new StringBuilder();
            sb.Append(
                "    <update id=\"" + this._tableName + "_UpdateByUnlimitedParameters\" parameterClass=\"" + _namespace + ".Parameters." + this._className + "Parameters," + _namespace + "\">" + Environment.NewLine
                + "        UPDATE " + this._tableName + Environment.NewLine
                + "        SET" + Environment.NewLine
                + "        <dynamic>" + Environment.NewLine
                );

            int listLng = this._col.Count;
            for (int i = 0; i < listLng; i++)
            {
                if (this._col[i].DataLength > this._lenghtForIgnoreParameter) continue;
                if (this._col[i] != this._col.PrimaryKeyColumn)
                {
                    sb.Append("          <isNotEmpty property=\"" + this._col[i].PropertyName + "\">" + Environment.NewLine);
                    sb.Append("          " + this._col[i].ColumnName + " = #" + this._col[i].PropertyName + "#," + Environment.NewLine);
                    sb.Append("          </isNotEmpty>" + Environment.NewLine);
                }
            }
            sb.Append("        </dynamic>" + Environment.NewLine);

            sb.Append("          " + this._col.PrimaryKeyColumn.ColumnName + " = " + this._col.PrimaryKeyColumn.ColumnName  + Environment.NewLine);
            sb.Append(this.UnlimitedWhere());
            sb.Append("    </update>" + Environment.NewLine);
            return sb.ToString();
        }

        private string Update(string where, string id, string paramClass)
        {
            if (String.IsNullOrEmpty(where)) return "";

            StringBuilder sb = new StringBuilder();
            sb.Append(
                "    <update id=\"" + id + "\" parameterClass=\"" + paramClass + "\">" + Environment.NewLine
                + "      <![CDATA[ " + Environment.NewLine
                + "        UPDATE " + this._tableName + Environment.NewLine
                + "        SET" + Environment.NewLine
                );
            int listLng = this._col.Count;
            for (int i = 0; i < listLng; i++)
            {
                if (this._col[i] != this._col.PrimaryKeyColumn)
                {
                    sb.Append("          " + this._col[i].ColumnName + " = #" + this._col[i].PropertyName + "#" + ((i == (listLng - 1)) ? "" : ",") + Environment.NewLine);
                }
            }
            sb.Append("      ]]>" + Environment.NewLine);
            sb.Append(where);
            sb.Append("    </update>" + Environment.NewLine);
            return sb.ToString();
        }
        #endregion

        #region where
        private string EntryWhere()
        {
            if (this._col.PrimaryKeyColumn == null) return null;
            StringBuilder sb = new StringBuilder();

            sb.Append("      <dynamic prepend=\"WHERE\">" + Environment.NewLine);
            sb.Append("          " + this._col.PrimaryKeyColumn.ColumnName + " = #" + this._col.PrimaryKeyColumn.PropertyName + "#" + Environment.NewLine);
            sb.Append("      </dynamic>" + Environment.NewLine);

            return sb.ToString();
        }

        private string ParameterWhere()
        {
            return this.MultiColumnWhere(this._col, false);
        }

        private string PrimaryKeyWhere()
        {
            if (this._col.PrimaryKeyColumn == null) return null;
            StringBuilder sb = new StringBuilder();

            sb.Append("      <dynamic prepend=\"WHERE\">" + Environment.NewLine);
            sb.Append("          " + this._col.PrimaryKeyColumn.ColumnName + " = #value#" + Environment.NewLine);
            sb.Append("      </dynamic>" + Environment.NewLine);

            return sb.ToString();
        }

        private string UnlimitedWhere()
        {
            StringBuilder sb = new StringBuilder();

            sb.Append("      <dynamic prepend=\"WHERE\">" + Environment.NewLine);

            //old
            //sb.Append("          1=2 " + Environment.NewLine);
            //sb.Append("        <isNotNull property=\"UnlimitedQuery\">" + Environment.NewLine);
            //sb.Append("          <![CDATA[ " + Environment.NewLine);
            //sb.Append("            OR ($UnlimitedQuery$)" + Environment.NewLine);
            //sb.Append("          ]]>" + Environment.NewLine);
            //sb.Append("        </isNotNull>" + Environment.NewLine);

            //new
            sb.Append("        <isNotEmpty property=\"UnlimitedQuery\">" + Environment.NewLine);
            sb.Append("          <![CDATA[ " + Environment.NewLine);
            sb.Append("            $UnlimitedQuery$" + Environment.NewLine);
            sb.Append("          ]]>" + Environment.NewLine);
            sb.Append("        </isNotEmpty>" + Environment.NewLine);
            sb.Append("        <isEmpty property=\"UnlimitedQuery\">" + Environment.NewLine);
            sb.Append("            1=2" + Environment.NewLine);
            sb.Append("        </isEmpty>" + Environment.NewLine);

            sb.Append("      </dynamic>" + Environment.NewLine);

            return sb.ToString();
        }

        private string MultiColumnWhere(ColumnMapCollection multiColumns, bool isAllColumnsForQuery)
        {
            StringBuilder sb = new StringBuilder();

            sb.Append("      <dynamic prepend=\"WHERE\">" + Environment.NewLine);
            for (int i = 0; i < multiColumns.Count; i++)
            {
                ColumnMap map = multiColumns[i];

                if (!isAllColumnsForQuery && map.DataLength > this._lenghtForIgnoreParameter) continue;

                if (isAllColumnsForQuery || map != this._col.PrimaryKeyColumn)
                {
                    sb.Append
                    (
                       "        <isNotNull prepend=\"AND\" property=\"" + map.PropertyName + "\">" + Environment.NewLine
                    + "          <![CDATA[ " + Environment.NewLine
                    + "          " + map.ColumnName + " = #" + map.PropertyName + "#" + Environment.NewLine
                    + "          ]]>" + Environment.NewLine
                    + "        </isNotNull>" + Environment.NewLine
                    )
                    ;
                }
            }
            sb.Append("      </dynamic>" + Environment.NewLine);

            return sb.ToString();
        }
        #endregion

        public void Dispose()
        {
            this._col.Dispose();
            this._methods.Dispose();
        }
    }
}
